package com.pay;


import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.pay.alipay.config.AliPayConfig;
import com.pay.alipay.util.AlipaySubmit;
import com.pay.bean.*;
import com.pay.weixin.WxPayConfig;
import com.pay.weixin.util.HttpUtil;
import com.pay.weixin.util.PayCommonUtil;
import com.pay.weixin.util.XMLUtil;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by ThreeManTeam on 2016/11/21.
 */
public class PayManager {

    private static PayManager instance;

    private PayManager() {
    }

    public synchronized static PayManager getInstance() {
        if (instance == null) {
            instance = new PayManager();
        }
        return instance;
    }


    /**
     * 支付宝在线支付
     *
     * @param orderCode
     * @param orderInfo
     * @param totalPrice
     * @param orderName
     * @param orderDescr
     * @param timeout    支付超时时长，单位：秒；
     * @return
     */
    public String aliPay(String orderCode,
                         String orderInfo,
                         BigDecimal totalPrice,
                         String orderName,
                         String orderDescr,
                         Integer timeout,
                         AliPayConfig aliPayConfig) {
        String sHtmlText = "";
        try {
            AliPayConfig config = aliPayConfig != null ? aliPayConfig : AliPayConfig.getInstance();

            //参数说明见：https://docs.open.alipay.com/62/104743/
            /*********************基本参数配置**********************/
            Map<String, String> sParaTemp = new HashMap<String, String>();
            sParaTemp.put("service", config.getService());
            sParaTemp.put("partner", config.getPartner());
            sParaTemp.put("_input_charset", config.getInputCharset());
            sParaTemp.put("notify_url", config.getNotifyUrl());
            sParaTemp.put("return_url", config.getReturnUrl());
            //sign_type 和 sign 两个参数由后面的函数自动配置。

            /*********************业务参数配置*********************/
            sParaTemp.put("out_trade_no", orderCode);
            sParaTemp.put("subject", orderName); //商品/订单名称。
            sParaTemp.put("payment_type", config.getPaymentType());
            sParaTemp.put("total_fee", totalPrice.setScale(2, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString());
            sParaTemp.put("seller_id", config.getSellerId());
            //支付超时时间，单位分钟。
            sParaTemp.put("it_b_pay", (timeout == null || timeout <= 0) ? config.getItBPay() : (Math.max(2, Math.abs(timeout) / 60) + "m"));

            /*********************扩展参数*************************/
            String body = new String(orderDescr.getBytes("ISO-8859-1"), "UTF-8");
            sParaTemp.put("body", body);
            sParaTemp.put("extra_common_param", orderInfo);
            if (config.getEnablePayMethod() != null && config.getEnablePayMethod().trim().length() > 0) {
                sParaTemp.put("enable_paymethod", config.getEnablePayMethod());
            }
            if (config.getDisablePayMethod() != null && config.getDisablePayMethod().trim().length() > 0) {
                sParaTemp.put("disable_paymethod", config.getDisablePayMethod());
            }

            /*********************防钓鱼参数***********************/
            String timestamp = AlipaySubmit.query_timestamp();
            String ip = config.getExternalInvokeIp();
            if (timestamp.length() > 0 && ip != null && ip.length() > 0) {
                sParaTemp.put("anti_phishing_key", timestamp);
                sParaTemp.put("exter_invoke_ip", ip);
            }

            //建立请求
            sHtmlText = AlipaySubmit.buildRequest(sParaTemp, "get", "确认");

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return sHtmlText;
    }

    /**
     * 支付宝分账接口。
     *
     * @param asRequest 分账请求对象。
     * @return
     */
    /*public boolean splitAccountForAliPay(AliPaySplitRequest asRequest) {
        try {
            AliPayConfig config = AliPayConfig.getInstance();

            AlipayClient alipayClient = new DefaultAlipayClient(config.getOrderQueryUrl(),
                    config.getAppId(), config.getPublicKey(), "JSON", "GBK",
                    AliPayConfig.DEFAULT_ALI_PAY_PUBLIC_KEY1, "RSA2");

            AlipayTradeOrderSettleRequest request = new AlipayTradeOrderSettleRequest();
            AlipayTradeOrderSettleModel model = new AlipayTradeOrderSettleModel();
            model.setOutRequestNo(asRequest.getOutRequestNo());//该笔分账的请求单号，每一次请求保证唯一
            model.setTradeNo(asRequest.getTradeNo());//分账的交易的支付宝交易号
            //声明OpenApiRoyaltyDetailInfoPojo方法用于传入royaltyParameters
            List<OpenApiRoyaltyDetailInfoPojo> royaltyParameters = new ArrayList<>();
            asRequest.getRoyaltyParameters().forEach(x -> {
                OpenApiRoyaltyDetailInfoPojo royaltyParameter = new OpenApiRoyaltyDetailInfoPojo();
                royaltyParameter.setTransOut(x.getTransOut());//分出账户,也就是商家的账户pid
                royaltyParameter.setTransIn(x.getTransIn());//分账金额收款账户pid
                royaltyParameter.setAmount(x.getAmount().toString());
                royaltyParameter.setAmountPercentage(x.getAmountPercentage());
                royaltyParameters.add(royaltyParameter);
            });

            model.setRoyaltyParameters(royaltyParameters);
            request.setBizModel(model);

            AlipayTradeOrderSettleResponse response = alipayClient.execute(request);
            //使用第三方应用授权app_auth_token传值方式
            //AlipayTradeOrderSettleResponse response = alipayClient.execute(request,null,"app_auth_token");
            if (response.isSuccess()) {
                System.out.println("调用成功");
                System.out.println(response.getBody());
            } else {
                System.out.println("调用失败");
                System.out.println(response.getBody());
            }
            return response.isSuccess();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }*/

    /**
     * 微信支付统一下单接口
     *
     * @param orderCode
     * @param orderInfo
     * @param totalPrice
     * @param description
     * @param timeout     支付超时时长，单位：秒；
     * @return
     */
    public String weiXinPay(String orderCode, String orderInfo,
                            BigDecimal totalPrice, String description,
                            Integer timeout, WxPayConfig wxPayConfig) throws Exception {

        WxPayConfig config = wxPayConfig != null ? wxPayConfig : WxPayConfig.getInstance();
        //账号信息
        String appid = config.getAppId();  // appid
        //商业号
        String mch_id = config.getMchId();
        // key
        String key = config.getKey();

        String currTime = PayCommonUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayCommonUtil.buildRandom(4) + "";
        //随机字符串
        String nonce_str = strTime + strRandom;

        // 获取发起电脑IP
        String spbill_create_ip = config.getIp();

        // 回调接口
        String notify_url = config.getNotifyUrlAsync();
        String trade_type = config.getTradeType();

        String time_start = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        Calendar ca = Calendar.getInstance();
        ca.setTime(new Date());

        timeout = (timeout == null || timeout <= 0) ? config.getPayTimeout() : timeout;

        ca.add(Calendar.MINUTE, Math.max(2, Math.abs(timeout / 60)));
        String time_expire = new SimpleDateFormat("yyyyMMddHHmmss").format(ca.getTime());

        //参数说明见：https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
        SortedMap<Object, Object> packageParams = new TreeMap<>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", description);
        packageParams.put("attach", orderInfo);
        packageParams.put("out_trade_no", orderCode);

        //价格的单位是分
        String total = "" + totalPrice.multiply(new BigDecimal(100)).intValue();
        packageParams.put("total_fee", total);
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);
        packageParams.put("time_start", time_start);
        packageParams.put("time_expire", time_expire);
        packageParams.put("trade_type", trade_type);

        //trade_type=NATIVE时（即扫码支付），此参数必传。
        if ("NATIVE".equals(trade_type)) {
            packageParams.put("product_id", orderCode);
        }

        //计算签名数据。
        String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
        packageParams.put("sign", sign);

        String requestXML = PayCommonUtil.getRequestXml(packageParams);

        String resXml = HttpUtil.postDataEx(config.PAY_API, requestXML);

        WxPrePayResult result = new WxPrePayResult();

        Map<String, String> map = null;
        try {
            map = XMLUtil.doXMLParse(resXml);
        } catch (Exception ex) {
            throw new Exception("微信支付接口返回的XML格式解析异常:" + ex.getMessage());
        }

        if (map == null)
            throw new Exception("微信支付接口返回的XML格式的键值集合为空");

        result.initial(map, key);

        if (!"SUCCESS".equals(result.getReturnCode()) || !result.isSignValid()) {
            throw new Exception(result.getReturnMsg());
        } else if (!"SUCCESS".equals(result.getResultCode())) {
            throw new Exception(result.getErrCodeDes());
        }

        return result.getCodeUrl();
    }

    /**
     * 微信支付的分账接口。
     *
     * @param wxSplitDetails
     * @return
     */
    public boolean splitAccountForWxPay(List<WxSplitDetail> wxSplitDetails) throws Exception {
        WxPayConfig config = WxPayConfig.getInstance();
        //账号信息
        String appid = config.getAppId();  // appid
        //商业号
        String mch_id = config.getMchId();
        // key
        String key = config.getKey();

        String currTime = PayCommonUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayCommonUtil.buildRandom(4) + "";
        //随机字符串
        String nonce_str = strTime + strRandom;

        //参数说明见：https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_1&index=1
        SortedMap<Object, Object> packageParams = new TreeMap<>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("sub_appid", mch_id);
        packageParams.put("sub_mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("transaction_id", "");
        packageParams.put("out_order_no", "");
        packageParams.put("sign_type", "HMAC-SHA256");

        List<SortedMap<Object, Object>> list = new ArrayList<>();
        packageParams.put("receivers", list);

        wxSplitDetails.forEach(x -> {
            SortedMap<Object, Object> receiverMap = new TreeMap<>();
            list.add(receiverMap);
            receiverMap.put("type", x.getType());
            receiverMap.put("account", x.getAccount());
            //价格的单位是分
            receiverMap.put("amount", x.getAmount());
            receiverMap.put("description", x.getDescription());
        });

        //计算签名数据。
        String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
        packageParams.put("sign", sign);

        String requestXML = PayCommonUtil.getRequestXml(packageParams);

        String resXml = HttpUtil.postDataEx(config.SPLIT_ACCOUNT_API, requestXML);

        WxPrePayResult result = new WxPrePayResult();

        Map<String, String> map = null;
        try {
            map = XMLUtil.doXMLParse(resXml);
        } catch (Exception ex) {
            throw new Exception("微信支付接口返回的XML格式解析异常:" + ex.getMessage());
        }

        if (map == null)
            throw new Exception("微信支付接口返回的XML格式的键值集合为空");

        result.initial(map, key);

        if (!result.isSignValid()) {
            throw new Exception("接收数据签名验证异常");
        } else if (!"SUCCESS".equals(result.getReturnCode())) {
            throw new Exception(result.getReturnMsg());
        }
        if (!"SUCCESS".equals(result.getResultCode())) {
            throw new Exception(result.getErrCodeDes());
        }

        return false;
    }

    /**
     * @param outTradeNo 商城交易编号。
     * @return
     */
    public IPayResult queryPayStatusFromAliPay(String outTradeNo, AliPayConfig config) {

        AliPayResultQueryRes queryRes = new AliPayResultQueryRes();

        try {
            if (config == null) {
                config = AliPayConfig.getInstance();
            }

            //初始化客户端对象。
            AlipayClient alipayClient = new DefaultAlipayClient(config.getOrderQueryUrl(),
                    config.getAppId(),
                    config.getPrivateKey(),
                    config.getFormat(),
                    config.getInputCharset(),
                    config.getAliPublicKey(),
                    config.getSignType());

            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
            model.setOutTradeNo(outTradeNo);

            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            request.setBizModel(model);

            AlipayTradeQueryResponse response = alipayClient.execute(request);

            /*
             * 交易状态：
             * WAIT_BUYER_PAY（交易创建，等待买家付款）、
             * TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、
             * TRADE_SUCCESS（交易支付成功）、
             * TRADE_FINISHED（交易结束，不可退款）
             */
            queryRes.setReqSuccess(response.isSuccess());
            queryRes.setVerifySuccess(true);
            queryRes.setTradeStatus(response.getTradeStatus());

            queryRes.setOutTradeNo(response.getOutTradeNo());
            queryRes.setTradeNo(response.getTradeNo());
            queryRes.setSellerId(config.getSellerId());
            queryRes.setBuyerId(response.getBuyerUserId());
            queryRes.setTotalFee(response.getTotalAmount() != null ? new BigDecimal(response.getTotalAmount()) : BigDecimal.ZERO);
            queryRes.setGmtPayment(response.getSendPayDate());

            queryRes.setBody(response.getBody());
            queryRes.setPaymentType(response.getPayCurrency());

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return queryRes;
    }

    /**
     * 查询微信订单支付状态。
     *
     * @param outTradeNo 商城交易编号。
     * @return
     */
    public IPayResult queryPayStatusFromWxPay(String outTradeNo) {
        WxPayConfig config = WxPayConfig.getInstance();
        //账号信息
        String appid = config.getAppId();  // appid
        //商业号
        String mch_id = config.getMchId();
        // key
        String key = config.getKey();

        String currTime = PayCommonUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayCommonUtil.buildRandom(4) + "";
        //随机字符串
        String nonce_str = strTime + strRandom;

        SortedMap<Object, Object> packageParams = new TreeMap<>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("out_trade_no", outTradeNo);
        packageParams.put("nonce_str", nonce_str);

        //计算签名数据。
        String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
        packageParams.put("sign", sign);

        WxPayResultQueryRes payResult = new WxPayResultQueryRes();
        try {
            String requestXML = PayCommonUtil.getRequestXml(packageParams);
            String resXml = HttpUtil.postDataEx(config.PAY_QUERY_API, requestXML);
            Map map = XMLUtil.doXMLParse(resXml);
            payResult.initial(map, key);
        } catch (Exception ignored) {
        }
        return payResult;
    }

    /****************************私有函数******************************/


}
